package com.g2room.search.es.engine;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.sort.SortBuilder;

import com.g2room.search.es.test.Constants;
import com.g2room.search.es.test.SearchParamTo;
import com.g2room.search.es.to.SearchResultPageTo;

public class Search {
	private static EsClient esClient = new EsClient("pee");
	
	public static enum QueryType {
		/** OR查询，值为~ */
		OR("~"),
		/** 值为+ */
		AND("+"),
		/** 排除符号，值为- */
		EXCL("-"),
		/** 前缀查询，值为# */
		PREFIX("#");
		private String val;
		QueryType(String val) {this.val =val;}
		public String val(){return this.val;}
	}

	/**
	 * 搜索数据并分页显示
	 * @param param	key为字段名，value为查询值。说明：<br>
	 * 		and查询，则字段名前加+或不加任何修饰；默认就是必须包含；<br>
	 * 		or查询，则字段名前加~；<br>
	 * 		排除查询，则字段名前加-；<br>
	 */
	public SearchResultPageTo search(SearchParamTo spt){
		SearchRequestBuilder searchRequestBuilder = esClient.getClient().prepareSearch(esClient.getIndex());
		searchRequestBuilder.setTypes(spt.getType().val())
			.setSearchType(SearchType.DFS_QUERY_THEN_FETCH)
			.setQuery(getQb(spt)).setFrom(spt.getStart())
			.setSize(spt.getSize()).setExplain(true);
		if(null != spt.getSort()) {
			searchRequestBuilder.addSort(spt.getSort());
		} else if(null != spt.getSorts()) {
			for(SortBuilder sort : spt.getSorts()) {
				searchRequestBuilder.addSort(sort);
			}
		}
		// 最后就是返回搜索响应信息
		SearchResponse response = searchRequestBuilder.execute().actionGet();
		SearchResultPageTo srpt = new  SearchResultPageTo();
		//获取搜索文档的结果
		SearchHits searchHits = response.getHits();
		srpt.setCount(searchHits.getTotalHits());
		SearchHit[] hits = searchHits.getHits();
		List<Map<String, Object>> resultList = new ArrayList<Map<String, Object>>();
		for (int i = 0; i < hits.length; i++) {
			SearchHit hit = hits[i];
			resultList.add(hit.getSource());
		}
		srpt.setResult(resultList);
		srpt.setSize(spt.getSize());
		srpt.setStart(spt.getStart());
		return srpt;
	}
	
	private QueryBuilder getQb(SearchParamTo spt) {
		//设置查询关键字
		BoolQueryBuilder bqb = QueryBuilders.boolQuery();
		if(null != spt.getParam() && !spt.getParam().isEmpty()) {
			for(String filed : spt.getParam().keySet()) {
				Object val = spt.getParam().get(filed);
				if(null == val) continue;
				// 获取第一个字符
				String firstChar = filed.substring(0, 1);
				if(firstChar.equals(QueryType.EXCL.val())) {
					bqb.mustNot(QueryBuilders.termQuery(filed.replace(QueryType.EXCL.val(), ""), val));
				} else if(firstChar.equals(QueryType.OR.val())) {
					bqb.should(QueryBuilders.termQuery(filed.replace(QueryType.OR.val(), ""), val));
				} else if(firstChar.equals(QueryType.PREFIX.val())) {
					bqb.should(QueryBuilders.prefixQuery(filed.replace(QueryType.PREFIX.val(), ""), val.toString()));
				} else {
					bqb.must(QueryBuilders.termQuery(filed.replace(QueryType.AND.val(), ""), val));
				}
				// 判断是否需要多值查询
				if(val instanceof String && val.toString().contains(Constants.COMMA)) {
					for(String v : val.toString().split(Constants.COMMA)) {
						bqb.should(QueryBuilders.termQuery(filed, v));
					}
				} else {
					bqb.must(QueryBuilders.termQuery(filed.replace(QueryType.AND.val(), ""), val));
				}
			}
		}
		if(null != spt.getExce() && !spt.getExce().isEmpty()) {
			for(String excl : spt.getExce()) {
				if(null == excl || !excl.contains(Constants.UNDER_LINE)) continue;
				String[] excls = excl.split(Constants.UNDER_LINE);
				bqb.mustNot(QueryBuilders.termQuery(excls[0], excls[1]));
			}
		}
		return bqb;
	}
	/**
	 * 根据ID查询对象信息
	 * @param id	对象ID
	 * @param type	索引TYPE
	 * @return	对象集合，key为属性，value为对于值
	 */
	public Map<String, Object> searchById(String id, IndexType type){
		SearchRequestBuilder searchRequestBuilder = getSearchRequest(esClient.getIndex(), type.val());
		//设置查询关键字
		searchRequestBuilder.setQuery(QueryBuilders.termQuery(EsClient.FIELD_ID, id));
		// 最后就是返回搜索响应信息
		SearchResponse response = searchRequestBuilder.execute().actionGet();

		//获取搜索文档的结果
		SearchHits searchHits = response.getHits();
		SearchHit[] hits = searchHits.getHits();
		return null == hits || hits.length == 0 ? null :  hits[0].getSource();
	}

	/**
	 * 根据ID集合查询文档ID
	 * @param ids	业务ID集合
	 * @param type	索引类型
	 * @return
	 */
	public List<String> searchDocIdByIds(List<String> ids, String type){
		SearchRequestBuilder searchRequestBuilder = getSearchRequest(esClient.getIndex(), type);
		BoolQueryBuilder bqb = QueryBuilders.boolQuery();
		for(String id : ids) {
			bqb.should(QueryBuilders.termQuery(EsClient.FIELD_ID, id));
		}
		searchRequestBuilder.setQuery(bqb);
		SearchResponse response = searchRequestBuilder.execute().actionGet();
	    SearchHits hits = response.getHits();
	    List<String> result = new ArrayList<String>();
	    for (SearchHit hit : hits.getHits()) {
	    	result.add(hit.getId());
	    }
	    return result;
	}

	/**
	 * 根据id查询文档ID
	 * @param id	业务表id
	 * @param type	索引类型
	 * @return	索引文档ID
	 */
	public String searchDocIdById(String id, String type){
		SearchRequestBuilder searchRequestBuilder = getSearchRequest(esClient.getIndex(), type);
		BoolQueryBuilder bqb = QueryBuilders.boolQuery();
		bqb.should(QueryBuilders.termQuery(EsClient.FIELD_ID, id));
		searchRequestBuilder.setQuery(bqb);
		SearchResponse response = searchRequestBuilder.execute().actionGet();
	    SearchHits hits = response.getHits();
	    List<String> result = new ArrayList<String>();
	    for (SearchHit hit : hits.getHits()) {
	    	result.add(hit.getId());
	    }
	    return hits.getHits().length==0?"":hits.getHits()[0].getId();
	}

	public List<Map<String, Object>> searchByIds(List<Long> steamIds, String type){
		SearchRequestBuilder searchRequestBuilder = getSearchRequest(esClient.getIndex(), type);
		BoolQueryBuilder bqb = QueryBuilders.boolQuery();
		for(Long id : steamIds) {
			bqb.should(QueryBuilders.termQuery(EsClient.FIELD_ID, id));
		}
		searchRequestBuilder.setQuery(bqb);
		SearchResponse response = searchRequestBuilder.execute().actionGet();
	    SearchHits hits = response.getHits();
	    List<Map<String, Object>> result = new LinkedList<Map<String, Object>>();
	    for (SearchHit hit : hits.getHits()) {
	    	result.add(hit.getSource());
	    }
	    return result;
	}
	
	public SearchRequestBuilder getSearchRequest(String index, String type) {
		//创建查询索引
		SearchRequestBuilder searchRequestBuilder = esClient.getClient().prepareSearch(index);
		//设置查询索引类型
		searchRequestBuilder.setTypes(type);
		// 设置查询数据的位置,分页用
		searchRequestBuilder.setFrom(0);
		// 设置查询结果集的最大条数
		searchRequestBuilder.setSize(1060);
		// 设置查询结果集的最大条数
		searchRequestBuilder.setRouting(EsClient.getRouting(type));
		//设置查询类型
		//1.SearchType.DFS_QUERY_THEN_FETCH = 精确查询
		// 2.SearchType.SCAN = 扫描查询,无序
		// 3.SearchType.COUNT = 不设置的话,这个为默认值,还有的自己去试试吧
		searchRequestBuilder.setSearchType(SearchType.DFS_QUERY_THEN_FETCH);
		return searchRequestBuilder;
	}
}
